package com.jackstraw.jack_base.util.function;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import com.jackstraw.jack_base.util.exception.BadConfigException;
import lombok.extern.slf4j.Slf4j;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class ReflectionUtil {

    /**
     * 缓存
     */
    private static Map<SerializableFunction<?, ?>, Field> serializableFunctionCache = new ConcurrentHashMap<>();
    /**
     * 缓存
     */
    private static final Map<Class<?>, Field[]> declaredFieldsCache = new ConcurrentHashMap<>();

    private static final Map<Class<?>, Map<Field, Class<?>>> declaredFieldTypesCache = new ConcurrentHashMap<>();

    private static final Field[] EMPTY_FIELD_ARRAY = new Field[0];


    /**
     * 获取某个对象的字段名称
     *
     * @param function
     * @param <T>
     * @param <R>
     * @return
     */
    public static <T, R> String getFieldName(SerializableFunction<T, R> function) {
        Field field = ReflectionUtil.getField(function);
        return field.getName();
    }

    /**
     * 获取某个对象字段的field
     *
     * @param function
     * @return
     */
    public static Field getField(SerializableFunction<?, ?> function) {
        return serializableFunctionCache.computeIfAbsent(function, ReflectionUtil::findField);
    }


    /**
     * 获取该Field
     *
     * @param function
     * @return
     */
    public static Field findField(SerializableFunction<?, ?> function) {
        Field field = null;
        String fieldName = null;
        try {
            // 第1步 获取SerializedLambda
            Method method = function.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) method.invoke(function);
            // 第2步 implMethodName 即为Field对应的Getter方法名
            String implMethodName = serializedLambda.getImplMethodName();
            if (implMethodName.startsWith("get") && implMethodName.length() > 3) {
                fieldName = Introspector.decapitalize(implMethodName.substring(3));

            } else if (implMethodName.startsWith("is") && implMethodName.length() > 2) {
                fieldName = Introspector.decapitalize(implMethodName.substring(2));
            } else if (implMethodName.startsWith("lambda$")) {
                throw new IllegalArgumentException("SerializableFunction不能传递lambda表达式,只能使用方法引用");

            } else {
                throw new IllegalArgumentException(implMethodName + "不是Getter方法引用");
            }
            // 第3步 获取的Class是字符串，并且包名是“/”分割，需要替换成“.”，才能获取到对应的Class对象
            String declaredClass = serializedLambda.getImplClass().replace("/", ".");
            Class<?> aClass = Class.forName(declaredClass, false, ClassUtil.getClassLoader());

            // 第4步 反射工具类获取Class中定义的Field
            field = findField(aClass, fieldName);

        } catch (Exception e) {
            e.printStackTrace();
        }
        // 第5步 如果没有找到对应的字段应该抛出异常
        if (field != null) {
            return field;
        }
        throw new NoSuchFieldError(fieldName);
    }

    /**
     * 尝试使用提供的{@code name}在提供的{@link Class}上找到一个{@link Field Field}。 搜索所有超类直到{@link Object}。
     *
     * @param clazz 自省类
     * @param name  字段的名称(如果指定type，可以是{@code null})
     * @return 对应的Field对象，或者如果没有找到{@code null}
     */
    public static Field findField(Class<?> clazz, String name) {
        return findField(clazz, name, null);
    }


    /**
     * 尝试使用提供的{@code name}和/或{@link Class type}在提供的{@link Class}上找到一个{@link Field Field}。 搜索所有超类直到{@link Object}。
     *
     * @param clazz 自省类
     * @param name  字段的名称(如果指定type，可以是{@code null})
     * @param type  字段的类型(如果指定了name，可以是{@code null})
     * @return 对应的Field对象，或者如果没有找到{@code null}
     */
    public static Field findField(Class<?> clazz, String name, Class<?> type) {
        Assert.notNull(clazz, "Class must not be null");
        Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified");
        List<Field> declaredFieldsAll = getDeclaredFieldsAll(clazz);
        Optional<Field> optionalField = declaredFieldsAll.stream().filter(field -> (name == null || name.equals(field.getName())) &&
                (type == null || type.equals(field.getType()))).findFirst();
        if (optionalField.isPresent()) {
            return optionalField.get();
        }
        return null;
    }

    /**
     * 这个变体从本地缓存中检索{@link Class#getDeclaredFields()}，以避免JVM的SecurityManager检查和防御数组复制。
     *
     * @param clazz 自省类
     * @return 缓存的字段数组
     */
    public static Field[] getDeclaredFields(Class<?> clazz) {
        Assert.notNull(clazz, "Class must not be null");
        Field[] result = declaredFieldsCache.get(clazz);
        if (result == null) {
            try {
                result = clazz.getDeclaredFields();
                declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
            } catch (Throwable ex) {
                throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
                        "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
            }
        }
        return result;
    }

    /**
     * 这个变体从本地缓存中检索{@link Class#getDeclaredFields()}，以避免JVM的SecurityManager检查和防御数组复制。
     *
     * @param clazz 自省类
     * @return 缓存的字段数组
     */
    public static List<Field> getDeclaredFieldsAll(Class<?> clazz) {
        Assert.notNull(clazz, "Class must not be null");
        Field[] result = declaredFieldsCache.get(clazz);
        if (result == null) {
            List<Field> fieldList = new ArrayList<>();
            Class<?> searchType = clazz;
            while (Object.class != searchType && searchType != null) {
                fieldList.addAll(Arrays.asList(getDeclaredFields(searchType)));
                searchType = searchType.getSuperclass();
            }
            result = fieldList.toArray(EMPTY_FIELD_ARRAY);
            declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));
        }
        return Arrays.asList(result);
    }


    /**
     * 获取对应字段的值
     *
     * @param f   字段
     * @param t   对象
     * @param <T>
     * @return
     */
    public static <T> Object getValue(String f, T t) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
            PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor descriptor : descriptors) {
                if (descriptor.getName().equals(f)) {
                    return descriptor.getReadMethod().invoke(t);
                }
            }
        } catch (Exception e) {
            throw new BadConfigException("001", "根据字段名获取属性值失败", e);
        }
        return null;
    }

    public static Class<?> classForName(String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获得超类的参数类型，取第一个参数类型
     *
     * @param <T>   类型参数
     * @param clazz 超类类型
     */
    @SuppressWarnings("rawtypes")
    public static <T> Class<T> getClassGenricType(final Class clazz) {
        return getClassGenricType(clazz, 0);
    }

    /**
     * 根据索引获得超类的参数类型
     *
     * @param clazz 超类类型
     * @param index 索引
     */
    @SuppressWarnings("rawtypes")
    public static Class getClassGenricType(final Class clazz, final int index) {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            log.warn(String.format("Warn: %s's superclass not ParameterizedType", clazz.getSimpleName()));
            return null;
        }

        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index <= params.length && index >= 0) {
            if ((params[index] instanceof Class)) {
                return (Class) params[index];
            } else if (ParameterizedType.class.isAssignableFrom(params[index].getClass())) {
                ParameterizedType parameterizedType = (ParameterizedType) params[index];
                Type rawType = parameterizedType.getRawType();
                if ((rawType instanceof Class)) {
                    return (Class) rawType;
                }
            }
            log.warn(String.format("Warn: %s not set the actual class on superclass generic parameter", clazz.getSimpleName()));
            return Object.class;
        } else {
            log.warn(String.format("Warn: Index: %s, Size of %s's Parameterized Type: %s .", index, clazz.getSimpleName(), params.length));
            return null;
        }
    }

    /**
     * 创建泛型对应的对象
     *
     * @param initargs
     * @return
     */
    public static Object newInstance(Class<?> clazz, Object... initargs) {
        return newInstance(clazz, 0, initargs);
    }

    /**
     * 创建泛型对应的对象
     *
     * @param initargs
     * @return
     */
    public static Object newInstance(Class<?> clazz, Integer index, Object... initargs) {
        Object o = null;
        try {
            o = getClassGenricType(clazz, index).getDeclaredConstructor().newInstance(initargs);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return o;
    }

}
